package interpreter;

import interpreter.bytecodes.ByteCode;
import interpreter.debugger.DebugCodeTable;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;

/**
 * Reads byte codes from file (line by line),
 * builds an instance of the class corresponding to the byte code given, and
 * adds the instance to a fresh Program object.
 * 
 * @author Kristofer Selbekk
 */
public class ByteCodeLoader {

    private Program prog;
    private CodeTable codeTable;
    private BufferedReader reader;

    /**
     * Constructs the ByteCodeLoader
     *
     * Opens file and creates Program-object
     * 
     * @param fileName The path to valid x-code file
     * @throws IOException If problem with file, throw IOException
     */
    public ByteCodeLoader( String fileName, boolean debug ) throws IOException {
        File file;
        if( debug ) {
            file = new File( fileName + ".x.debug.cod");
            codeTable = new DebugCodeTable();
        } else {
            file = new File( fileName + ".x.cod" );
            codeTable = new CodeTable();
        }

        // ERROR CHECKING
        if( !file.exists() || !file.canRead() || !file.isFile() )
            throw new IOException();

        // Creates Program element
        prog = new Program();

        // Saves the file name just in case
        prog.setPath(fileName);

        // Opens file stream
        reader = new BufferedReader(new FileReader(file));
    }

    /**
     * Reads file line by line, parses data, instantiates the correct ByteCode-object
     * and adds this to a Program object.
     *
     * Before returning the Program-object, symbolic addresses are resolved.
     *
     * @return The Program-object, filled with freshly instantiated ByteCodes
     */
    public Program loadCodes() {
        // Loads byte codes from file
        String inLine;
       
        // Handles file line by line
        try {
            while( (inLine = reader.readLine()) != null ) {
                // Handles raw input
                String[] tokens = inLine.split("\\s");
                String codeClass = codeTable.get(tokens[0]);
                ByteCode bc;

                // Creates class from input
                try {
                    bc = (ByteCode) (Class.forName("interpreter.bytecodes."+ codeClass).newInstance());
                    // Creates argument vector (regardless if there are any)
                    Vector<String> args = new Vector<String>();
                    for( int i = 1; i < tokens.length; i++ )
                        args.addElement( tokens[i] );

                    // Initializes the byte code
                    bc.init(args);

                    // Adds byte code to program
                    prog.addCode( bc );


                } catch(ClassNotFoundException cnf) {
                    System.out.println("Error: Illegal byte code, ClassNotFoundException");
                    System.exit(-1);
                } catch(InstantiationException ie) {
                    System.out.println("Error: InstantiationException");
                    System.exit(-1);
                } catch(IllegalAccessException ia ) {
                    System.out.println("Error: IllegalAccessException");
                    System.exit(-1);
                }
            }
            reader.close();
        } catch( IOException io ) {
            System.out.println("Error: IOException");
            System.exit(-1);
        }
        // Resolves all addresses
        prog.resolveAddr();

        // Return generated program object
        return prog;
    }
}
